#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>
#include "task.hpp"
#include <thread>
#include <mutex>
#include <condition_variable>

const size_t default_size = 10;

template<class T>
class thread_pool
{
public:
    thread_pool(int size = default_size) : _cap(size)
    {}

    ~thread_pool()
    {}

    void start()
    {
        for (int i = 0; i < _cap ; i++)
        {
            _ths.emplace_back(routine, this);
            _ths[i].detach();
        }
    }

    static void* routine(void* args)
    {
        thread_pool* ts = static_cast<thread_pool*>(args);

        while (true)
        {
            T t;
            ts->pop_task(&t);
            t();
            std::cout << t.format_result() << std::endl;
        }
    }

    void push_task(const T& data)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        _tasks.push(data);
        _cv.notify_all();
    }

    void pop_task(T* data)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        _cv.wait(lock, [&](){ return !_tasks.empty(); });

        *data = _tasks.front();
        _tasks.pop();
    }

private:
    std::vector<std::thread> _ths;
    size_t _cap;

    std::queue<T> _tasks;

    std::mutex _mtx;
    std::condition_variable _cv;

};
